package rexsee.cartoon.component;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Random;

import rexsee.cartoon.Action;
import rexsee.cartoon.Actions;
import rexsee.cartoon.CartoonViewTouchListener;
import rexsee.cartoon.CartoonViewTouchListener.CartoonTouchListener;
import rexsee.cartoon.CartoonViewTouchListener.TouchEvent;
import rexsee.cartoon.Cartoons;
import rexsee.cartoon.Frame;
import rexsee.cartoon.Parser;
import rexsee.cartoon.Position;
import rexsee.cartoon.TagName;
import rexsee.core.utilities.Utilities;
import rexsee.xml.XmlElement;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Region;

public class Cartoon {

	public final Parser parser;

	private String id = null;
	protected Action action = null;
	protected Action pressedAction = null;
	protected Position position = null;
	protected final ArrayList<Position> movePositions = new ArrayList<Position>();

	public boolean play = true;
	public boolean visible = true;
	public boolean blockTouch = false;
	public boolean blockMove = false;;
	public boolean blockedReport = false;
	public boolean dragable = false;
	public boolean clickable = false;

	public String matrixStr = null;
	public Path clip = null;
	public Paint clipBorder = null;

	private boolean isDraging = false;
	private int startX = 0;
	private int startY = 0;
	private int currentFrame = -1;

	private final boolean isSupportClipPath = Cartoon.isSupportClipPath();

	public Cartoon(Parser parser, XmlElement element) {

		this.parser = parser;
		if (element == null || element.attributes == null) return;

		id = (element.attributes.get(TagName.ATTR_ID) != null) ? element.attributes.get(TagName.ATTR_ID) : "cartoon_" + (new Random().nextInt());
		visible = !"hidden".equalsIgnoreCase(element.attributes.get(TagName.ATTR_CARTOON_VISIBILITY));
		position = new Position().parse(element.attributes.get(TagName.ATTR_CARTOON_POSITION));
		matrixStr = element.attributes.get(TagName.ATTR_MATRIX);
		clip = Actions.getPath(element.attributes.get(TagName.ATTR_CLIP));
		clipBorder = Actions.getBorder(element.attributes.get(TagName.ATTR_CLIPBORDER));
		blockTouch = "true".equalsIgnoreCase(element.attributes.get(TagName.ATTR_CARTOON_BLOCKTOUCH));
		blockMove = "true".equalsIgnoreCase(element.attributes.get(TagName.ATTR_CARTOON_BLOCKMOVE));
		blockedReport = "true".equalsIgnoreCase(element.attributes.get(TagName.ATTR_CARTOON_BLOCK_REPORT));

		dragable = "true".equalsIgnoreCase(element.attributes.get(TagName.ATTR_CARTOON_DRAGABLE));
		clickable = "true".equalsIgnoreCase(element.attributes.get(TagName.ATTR_CARTOON_CLICKABLE));

		action = parser.actions.getAction(element.attributes.get(TagName.ATTR_CARTOON_ACTION));
		pressedAction = parser.actions.getAction(element.attributes.get(TagName.ATTR_CARTOON_PRESSEDACTION));

		if (action != null) action.load();
		if (pressedAction != null) pressedAction.load();

	}

	public Action getCurrentAction() {
		return ((dragable || clickable) && isDraging && pressedAction != null) ? pressedAction : action;
	}

	public void draw(Canvas canvas) {

		if (!visible) return;

		Action act = getCurrentAction();
		if (act == null) return;

		if (play) currentFrame++;
		if (currentFrame < 0 || currentFrame > act.list.size() - 1) currentFrame = 0;
		Frame frame = act.list.get(currentFrame);
		if (frame == null || frame.bitmap == null) return;

		canvas.save();
		if (clip != null && isSupportClipPath) canvas.clipPath(clip);
		if (matrixStr != null) {
			Matrix matrix = Actions.getMatrix(matrixStr, canvas.getWidth(), canvas.getHeight());
			if (matrix != null) canvas.setMatrix(matrix);
		}

		if (movePositions.size() > 0) {
			Position newPosition = movePositions.get(0);
			movePositions.remove(0);
			boolean finished = movePositions.size() == 0;
			finished = moveTo(newPosition) && finished;
			if (finished) parser.browser.eventList.run(Cartoons.EVENT_MOVE_FINISHED, new String[]{getId(), act.id, String.valueOf(currentFrame)});
		}
		canvas.drawBitmap(frame.bitmap, null, position.rect, null);

		canvas.restore();
		if (clip != null && clipBorder != null) canvas.drawPath(clip, clipBorder);

		if (frame.reportPlay) parser.browser.eventList.run(Cartoons.EVENT_FRAME_PLAY, new String[]{getId(), act.id, String.valueOf(currentFrame)});

	}

	public CartoonTouchListener getCartoonTouchListener() {
		if (dragable || clickable) {
			return new CartoonTouchListener() {
				@Override
				public boolean onEvent(TouchEvent event) {

					if (!visible) return false;

					boolean inRect = position.rect.contains(event.x, event.y);

					if (event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONTOUCHDOWN)) {
						if (inRect && !isDraging) {
							isDraging = true;
							startX = event.x;
							startY = event.y;
						}
						if (blockTouch && (inRect || isDraging)) return true;
					} else if (event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONTOUCHMOVE)) {
						if (isDraging) {
							if (dragable) moveTo(position.rect.left + event.x - startX, position.rect.top + event.y - startY);
							startX = event.x;
							startY = event.y;
						}
						if (blockTouch && isDraging) return true;
					} else if (event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONTOUCHUP) || event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONTOUCHCANCEL)) {
						if (isDraging) {
							isDraging = false;
							startX = 0;
							startY = 0;
						}
						if (blockTouch && inRect) return true;
					} else if (event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONCLICK)) {
						if (inRect && clickable) {
							parser.browser.eventList.run(CartoonViewTouchListener.EVENT_ONCLICK, new String[]{getId(), getCurrentAction().id, String.valueOf(currentFrame), String.valueOf(event.x), String.valueOf(event.y)});
						}
						if (blockTouch && inRect) return true;
					} else if (event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONDOUBLECLICK)) {
						if (blockTouch && inRect) return true;
					} else if (event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONLONGPRESS)) {
						if (blockTouch && inRect) return true;
					} else if (event.action.equalsIgnoreCase(CartoonViewTouchListener.EVENT_ONFLING)) {
						if (blockTouch && inRect) return true;
					}
					return false;
				}
				@Override
				public int getZIndex() {
					return position.z_index;
				}

			};
		} else {
			return new CartoonTouchListener() {
				@Override
				public boolean onEvent(TouchEvent event) {
					if (!visible) return false;
					boolean inRect = position.rect.contains(event.x, event.y);
					return blockTouch && inRect;
				}
				@Override
				public int getZIndex() {
					return position.z_index;
				}

			};
		}
	}

	public void destroy() {
	}

	public final String getId() {
		return id;
	}
	public String getAction() {
		if (parser == null || action == null) return null;
		return action.id;
	}
	public void setAction(String actionId) {
		if (actionId == null) return;
		if (parser == null) return;
		Action a = parser.actions.getAction(actionId);
		if (a == null) return;
		a.load();
		action = a;
	}
	public String getPressedAction() {
		if (parser == null || pressedAction == null) return null;
		return pressedAction.id;
	}
	public void setPressedAction(String actionId) {
		if (actionId == null) pressedAction = null;
		Action a = parser.actions.getAction(actionId);
		if (a != null) a.load();
		pressedAction = a;
	}

	public final void setZIndex(int zIndex) {
		position.z_index = zIndex;
	}
	public final int getZIndex() {
		return position.z_index;

	}
	public final void setPosition(int left, int top, int right, int bottom) {
		Position newPosition = new Position();
		newPosition.rect.left = left;
		newPosition.rect.top = top;
		newPosition.rect.right = right;
		newPosition.rect.bottom = bottom;
		newPosition.z_index = position.z_index;
		position = newPosition;
	}
	public final void moveTo(int x, int y) {
		Position p = position.clone();
		p.rect.left = x;
		p.rect.top = y;
		p.rect.right = p.rect.left + position.rect.width();
		p.rect.bottom = p.rect.top + position.rect.height();
		parser.cartoons.sortByZ();
		moveTo(p);
	}
	public final boolean moveTo(Position newPosition) {
		for (int i = parser.cartoons.size() - 1; i >= 0; i--) {
			Cartoon c = parser.cartoons.get(i);
			if (c == null || !c.visible || c.equals(this)) continue;
			if (!c.blockMove && c.position.rect.contains(newPosition.rect)) break;
			if (c.blockMove && c.position.rect.intersects(newPosition.rect.left, newPosition.rect.top, newPosition.rect.right, newPosition.rect.bottom)) {
				movePositions.clear();
				Action act = getCurrentAction();
				if (act != null && blockedReport) {
					parser.browser.eventList.run(Cartoons.EVENT_MOVE_BLOCKED, new String[]{getId(), act.id, String.valueOf(currentFrame), c.getId()});
				}
				position = getClosestPosition(c.position, position, newPosition);
				return false;
			}
		}
		position = newPosition;
		return true;
	}

	private int getMinOffset(int current, int target) {
		if (target > current) {
			return 1;
		} else if (target < current) {
			return -1;
		} else {
			return 0;
		}
	}
	private Position getClosestPosition(Position host, Position current, Position target) {
		Position rtn = target.clone();
		while (host.rect.intersects(rtn.rect.left, rtn.rect.top, rtn.rect.right, rtn.rect.bottom)) {
			rtn.rect.left -= getMinOffset(current.rect.left, target.rect.left);
			rtn.rect.top -= getMinOffset(current.rect.top, target.rect.top);
			rtn.rect.right = rtn.rect.left + current.rect.width();
			rtn.rect.bottom = rtn.rect.top + current.rect.height();
		}
		return rtn;
	}

	public final void moveToToByStepSize(int x, int y, int stepSize) {
		int stepNumber = Math.max(Math.abs((x - position.rect.left) / stepSize), Math.abs((y - position.rect.top) / stepSize));
		moveToToByStepNumber(x, y, stepNumber);
	}
	public final void moveToToByStepNumber(int x, int y, int stepNumber) {
		movePositions.clear();
		int xDis = (x - position.rect.left) / stepNumber;
		int yDis = (y - position.rect.top) / stepNumber;
		for (int i = 1; i <= stepNumber; i++) {
			Position p = position.clone();
			if (i == stepNumber) {
				p.rect.left = x;
				p.rect.top = y;
			} else {
				p.rect.left = position.rect.left + xDis * i;
				p.rect.top = position.rect.top + yDis * i;
			}
			p.rect.right = p.rect.left + position.rect.width();
			p.rect.bottom = p.rect.top + position.rect.height();
			movePositions.add(p);
		}
		parser.cartoons.sortByZ();
	}
	public final void moveTo(String positions) {
		movePositions.clear();
		positions = positions.replace(" ", "");
		String[] s = positions.split("\\),");
		if (s.length == 0) return;
		for (int j = 0; j < s.length; j++) {
			String pair = s[j];
			while (pair.endsWith(")")) {
				pair = pair.substring(0, pair.length() - 1);
			}
			while (pair.startsWith("(")) {
				pair = pair.substring(1);
			}
			int[] pairInts = Utilities.getIntArray(pair, 0);
			if (pairInts == null) {
			} else if (pairInts.length == 2) {
				Position p = position.clone();
				p.rect.left = pairInts[0];
				p.rect.top = pairInts[1];
				p.rect.right = pairInts[0] + position.rect.width();
				p.rect.bottom = pairInts[1] + position.rect.height();
				movePositions.add(p);
			} else if (pairInts.length == 4) {
				Position p = position.clone();
				p.rect.left = pairInts[0];
				p.rect.top = pairInts[1];
				p.rect.right = pairInts[2];
				p.rect.bottom = pairInts[3];
				movePositions.add(p);
			} else if (pairInts.length == 5) {
				Position p = new Position();
				p.rect.left = pairInts[0];
				p.rect.top = pairInts[1];
				p.rect.right = pairInts[2];
				p.rect.bottom = pairInts[3];
				p.z_index = pairInts[4];
				movePositions.add(p);
			}
		}
		parser.cartoons.sortByZ();
	}
	public final Position getPosition() {
		return position;
	}

	public static class CompratorForCartoon
			implements
			Comparator<Cartoon> {
		@Override
		public int compare(Cartoon cartoon1, Cartoon cartoon2) {
			long diff = cartoon1.getZIndex() - cartoon2.getZIndex();
			if (diff > 0) return 1;
			else if (diff == 0) return 0;
			else return -1;
		}
		@Override
		public boolean equals(Object obj) {
			return true;
		}
	}

	public static boolean isSupportClipPath() {
		Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(bitmap);
		Path path = new Path();
		path.moveTo(0, 0);
		path.lineTo(100, 0);
		path.lineTo(0, 0);
		try {
			canvas.clipPath(path, Region.Op.XOR);
			bitmap.recycle();
			return true;
		} catch (Exception e) {
			bitmap.recycle();
			return false;
		}

	}

}
